import './App.css'
import { useEffect, useRef } from 'react';
import * as T from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { Line, Dot } from './utils';
import { bus } from './store';
import china from '@/assets/china.json';
import { gsap } from 'gsap';
import * as d3 from 'd3';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
import LeftTop from './table_lt';
import LeftBottom from './table_lb';
import RightTop from './table_rt';
import Header from './header';
import Cities from '@/assets/cities.json';
import Border from './dataV/border';
function App() {
  const app = useRef<HTMLDivElement>() as React.MutableRefObject<HTMLDivElement>
  const renderer = new T.WebGLRenderer({ alpha: true })
  const scene = new T.Scene()
  const clock = new T.Clock()
  let Id: number
  const camera = new T.PerspectiveCamera()
  camera.position.set(0, 700, 700)
  camera.lookAt(0, 0, 0)
  const control = new OrbitControls(camera, renderer.domElement);
  control.enableDamping = true
  control.autoRotate = true
  scene.add(new T.AmbientLight('rgb(214,214,214)'))
  const render = () => {
    ray.setFromCamera(new T.Vector2(pointer.x, pointer.y), camera)
    renderer.render(scene, camera)
    animaion(clock.getDelta())
    control.update()
    Id = requestAnimationFrame(render)
  }
  let animaion = (time: number) => {
    time = time + 1
    group.children.forEach(map => {
      if ((map as T.Mesh).isMesh) {
        ((map as T.Mesh).material as T.MeshBasicMaterial).opacity = 0.6
      }
    })
    const objs = ray.intersectObjects(group.children)[0]
    document.body.style.cursor = 'initial'
    if (objs) {
      ((objs.object as T.Mesh).material as T.MeshBasicMaterial).opacity = 1
      document.body.style.cursor = 'pointer'
    }
  }
  const resize = (app: HTMLElement) => {
    camera.aspect = app.offsetWidth / app.offsetHeight;
    renderer.setSize(app.offsetWidth, app.offsetHeight)
    camera.updateProjectionMatrix()
  }
  bus.on('pause', () => {
    control.autoRotate = !control.autoRotate
  })

  // 地图处理
  const projection = d3.geoMercator().scale(800)
  const cities = [] as { name: string, center: [number, number], x: number, y: number }[];
  const group = new T.Group();
  const cityPosition = [] as { name: string, position: [number, number] }[]
  china.features.forEach(city => {
    if (!!city.properties.name.length) {
      // 数据处理存储
      let { name, centroid: center } = city.properties
      if (!center) {
        center = city.properties.center
      }
      const [x, y] = projection(center as [number, number]) as [number, number]
      const data = { name, center, x, y }
      cities.push(data as { name: string, center: [number, number], x: number, y: number })

      // 地图数据处理加渲染
      const coords = name == '内蒙古自治区' ? city.geometry.coordinates[0] : city.geometry.coordinates[0][0];

      const shape = new T.Shape();
      for (let i = 0; i < coords.length; i++) {
        const [x, y] = projection(coords[i] as [number, number]) as [number, number]
        if (i > 0) {
          shape.lineTo(x, -y)
        } else {
          shape.moveTo(x, -y)
        }
      }
      const geo = new T.ExtrudeGeometry(shape, { depth: 10 });
      geo.name = name;
      const map = new T.Mesh(geo, new T.MeshBasicMaterial({ color: Math.random() * 0xffffff, transparent: true, opacity: 0.6 }));
      map.name = name
      group.add(map)
    }
  });
  const [gloableX, gloableY] = projection([103, 36]) as [number, number]//兰州，中国陆地的几何中心
  group.position.set(-gloableX, 0, -gloableY)
  group.rotateOnWorldAxis(new T.Vector3(1, 0, 0), -Math.PI / 2)
  scene.add(group)

  // 射线检测
  const ray = new T.Raycaster();
  let cityNow: string
  const pointer = { x: 0, y: 0 }
  renderer.domElement.addEventListener('pointermove', function (e) {
    pointer.x = (e.offsetX / this.offsetWidth) * 2 - 1
    pointer.y = - (e.offsetY / this.offsetHeight) * 2 + 1;
  })
  renderer.domElement.addEventListener('click', () => {
    const objs = ray.intersectObjects(group.children)[0]
    if (objs) {
      const city = cities.find(v => {
        return v.name == objs.object.name
      })
      if (city) {
        const x = city.x - gloableX
        const z = city.y - gloableY;
        if (cityNow && cityNow == city.name) {
          gsap.to(control.target, { x: 0, y: 0, z: 0 }); removeDots(); removePlane();
          refresh = setInterval(() => {
            const provinceIndex = Math.floor(Math.random() * cities.length)
            aotuInit(cities[provinceIndex].name)
          }, 3000);
        } else {
          gsap.to(control.target, { x, y: 0, z }); city && initProvince(city.name); cityNow = city.name
          bus.emit('area', city.name)
          refresh && clearInterval(refresh)
        }
      }
    }
  })

  // 航线部分
  const lines: T.Points[] = []
  const plane = new OBJLoader().loadAsync('./plane.obj')
  const planes: T.Group[] = [];
  const dots: T.Mesh[] = [];
  const addPlane = async (group: T.Group, points: [[number, number], [number, number]]) => {
    if (planes.length) {
      const newPlane = planes[0].clone()
      const [startx, starty] = projection(points[0]) as [number, number]
      try {
        const [endx, endy] = projection(points[1]) as [number, number]
        const line = new Line(new T.Vector3(startx, -starty, 0), new T.Vector3(endx, -endy, 0))
        lines.push(line.line)
        const dot = new Dot(5, '#71DC62');
        dot.dot.position.copy(new T.Vector3(endx, -endy, 11))
        const linePoints = line.curve.getPoints(80)
        newPlane.position.copy(linePoints[60])
        newPlane.lookAt(linePoints[59])
        newPlane.rotateZ(-Math.PI / 2)
        planes.push(newPlane)
        dot.flicker(1, 5)
        dots.push(dot.dot)
        group.add(line.line, dot.dot, newPlane)
      } catch (error) {
      }
    } else {
      const newPlane = await plane;
      newPlane.scale.set(0.3, 0.3, 0.3)
      planes.push(newPlane)
      addPlane(group, points)
    }
  }
  const removePlane = () => {
    //清空在场的飞机、航线、
    let [initPlane, ...otherPlanes] = planes;
    initPlane;
    group.remove(...otherPlanes)
    planes.splice(1, planes.length - 1)
    group.remove(...lines)
    lines.splice(0, lines.length)
    console.log(1);

  }
  const removeDots = () => {
    // 清空闪烁点
    group.remove(...dots)
    dots.splice(0, dots.length)
  }
  const initProvince = (province: string) => {
    removePlane()
    removeDots()
    if (!cityPosition.length) {//整理所有城市坐标
      Cities.province.forEach(i => {
        let data = i[Object.keys(i)[0]]
        data.forEach(c => {
          let name = Object.keys(c)[0]
          let position = c[name]
          cityPosition.push({ name, position })
        })
      })
    }
    const provinceCity = Cities.province.find(i => {
      return i[province]
    })
    const cities = provinceCity && provinceCity[province].map((i: any) => {
      return { position: i[Object.keys(i)[0]], name: Object.keys(i)[0] }
    })
    bus.emit('refreshLine')
    bus.emit('area', province)
    cities.forEach((i: any) => {//省内有机场的城市
      const dot = new Dot(5, 'red')
      dot.flicker(1, 3)
      const [x, y] = projection(i.position) as [number, number];
      dot.dot.position.copy(new T.Vector3(x, -y, 11))
      dots.push(dot.dot)
      group.add(dot.dot)
      import('./assets/route/' + i.name + '.json').then(lineData => {
        const forCities = lineData.default as { name: string, lines: { "航空公司": string, "飞机编号": string, "起飞时间": string, "起飞机场": string, "到达时间": string, "到达机场": string, "准点概率": string }[] }[];//这个城市可以飞往的城市
        forCities.forEach(item => {//将所有的城市遍历出来飞线
          const endPoint = cityPosition.find(ci => {
            return ci.name == item.name
          })?.position
          endPoint && addPlane(group, [i.position, endPoint as [number, number]])
        })
        bus.emit('addLine', forCities)
      })
    })
  }
  /**
   * 自动切换城市函数，初始化点但是不初始化函数
   */
  const aotuInit = (province: string) => {
    removePlane()
    removeDots()
    const provinceCity = Cities.province.find(i => {
      return i[province]
    })
    const cities = provinceCity && provinceCity[province].map((i: any) => {
      return { position: i[Object.keys(i)[0]], name: Object.keys(i)[0] }
    })
    cities.forEach((i: any) => {//省内有机场的城市
      const dot = new Dot(5, 'white')
      dot.flicker(1, 3)
      const [x, y] = projection(i.position) as [number, number];
      dot.dot.position.copy(new T.Vector3(x, -y, 11))
      dots.push(dot.dot)
      group.add(dot.dot)
      import('./assets/route/' + i.name + '.json').then(lineData => {
        const forCities = lineData.default as { name: string, lines: { "航空公司": string, "飞机编号": string, "起飞时间": string, "起飞机场": string, "到达时间": string, "到达机场": string, "准点概率": string }[] }[];//这个城市可以飞往的城市
        forCities.forEach(item => {//将所有的城市遍历出来飞线
          const endPoint = cityPosition.find(ci => {
            return ci.name == item.name
          })?.position
          endPoint && addPlane(group, [i.position, endPoint as [number, number]])
        })
        bus.emit('addLine', forCities)
      })
    })
    bus.emit('refreshLine')
    bus.emit('area', province)
  }
  var refresh = setInterval(() => {
    const provinceIndex = Math.floor(Math.random() * cities.length)
    aotuInit(cities[provinceIndex].name)
  }, 3000);



  useEffect(() => {
    app.current.appendChild(renderer.domElement)
    resize(app.current)
    render()
    window.addEventListener('resize', () => {
      resize(app.current)
    })
    return () => {
      cancelAnimationFrame(Id)
      window.removeEventListener('resize', () => {
        resize(app.current)
      })
    }
  })
  return (
    <>
      <Header></Header>
      <Border className="map">
        <div ref={app} style={{ width: '100%', height: '100%' }}></div>
      </Border>
      <LeftTop></LeftTop>
      <LeftBottom></LeftBottom>
      <RightTop></RightTop>
    </>
  )
}

export default App
